package xyz.xtt.pay.service.impl;

import static xyz.xtt.common.asset.AssertUtils.isNull;
import static xyz.xtt.pay.utils.AssertUtils.isFalse;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.asset.client.response.AssetAllInfoResponse;
import xyz.xtt.equity.api.dto.GetBenefitPurchaseRecordRequest;
import xyz.xtt.equity.api.dto.GetOnSaleBenefitPackageRequest;
import xyz.xtt.equity.api.dto.SaveBenefitPurchaseUnPayDto;
import xyz.xtt.equity.api.dto.TblBenefitPackageDto;
import xyz.xtt.equity.api.dto.TblBenefitPurchaseRecordDto;
import xyz.xtt.equity.api.enums.BenefitPurchasePayStatusEnum;
import xyz.xtt.equity.api.enums.TblBenefitPackageEnum;
import xyz.xtt.equity.client.request.GetUnPayBenefitPurchaseRecordRequest;
import xyz.xtt.equity.client.request.HandlePayCancelRequest;
import xyz.xtt.equity.client.request.HandlePaySuccessRequest;
import xyz.xtt.pay.api.dto.PayAttach;
import xyz.xtt.pay.api.enums.PayOrderBusinessTypeEnum;
import xyz.xtt.pay.api.enums.PayOrderStatusEnum;
import xyz.xtt.pay.constants.ErrorMessage;
import xyz.xtt.pay.dto.pay.GoodsInfo;
import xyz.xtt.pay.entity.TblPayOrder;
import xyz.xtt.pay.entity.TblPayOrderLog;
import xyz.xtt.pay.exception.ChainException;
import xyz.xtt.pay.service.IInnerService;
import xyz.xtt.pay.service.INftOrderService;
import xyz.xtt.pay.service.IPayOrderOperator;
import xyz.xtt.pay.service.IRocketMqService;
import xyz.xtt.pay.service.ITblPayOrderLogService;
import xyz.xtt.pay.service.ITblPayOrderService;
import xyz.xtt.pay.support.PayCenterHttpService;
import xyz.xtt.pay.utils.RedisLockUtil;
import xyz.xtt.pay.vo.CreateOrderRequest;
import xyz.xtt.pay.vo.PayCallbackMessage;
import xyz.xtt.pay.vo.PayCenterArgsResponse;
import xyz.xtt.user.client.response.UserInfoResponse;

/**
 * 权益包售卖处理器
 *
 * @author dale
 * @date 2025/2/10
 **/
@Component
@Slf4j
public class BenefitPackagePayOrderOperator implements IPayOrderOperator {
	@Autowired
	private PayCenterHttpService payCenterHttpService;
	@Autowired
	private RedisLockUtil redisLockUtil;
	@Autowired
	private ITblPayOrderService iTblPayOrderService;
	@Autowired
	private INftOrderService nftOrderService;
	@Autowired
	private IRocketMqService rocketMqService;
	@Autowired
	private ITblPayOrderLogService iTblPayOrderLogService;

	@Value("${personal.mint.order.price}")
	private String personalMintPrice;

	@Autowired
	private IInnerService iInnerService;

	@Override
	public boolean canOperateType(PayOrderBusinessTypeEnum type) {
		return Objects.equals(PayOrderBusinessTypeEnum.NFT_VIP_MONTH, type) || Objects.equals(PayOrderBusinessTypeEnum.NFT_VIP_SEASON, type)
				|| Objects.equals(PayOrderBusinessTypeEnum.NFT_VIP_YEAR, type);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public PayCenterArgsResponse createPayOrder(UserInfoResponse tblUser, PayOrderBusinessTypeEnum orderTypeEnum, CreateOrderRequest request,
			GoodsInfo goodsInfo) {
		// 检查是否有指定类型的权益包在售卖中
		TblBenefitPackageEnum benefitPackageEnum = TblBenefitPackageEnum.valueOf(orderTypeEnum.name());
		GetOnSaleBenefitPackageRequest onSaleBenefitPackageRequest = new GetOnSaleBenefitPackageRequest();
		onSaleBenefitPackageRequest.setPackageCode(benefitPackageEnum.name());
		TblBenefitPackageDto packageDto = iInnerService.getOnSaleBenefitPackage(onSaleBenefitPackageRequest);
		isNull(packageDto, ErrorMessage.ORDER_CREATE_FAILED, () -> log
				.info("NftVipPayOrderCreator createPayOrder getOnSaleBenefitPackage failed. uid:{} param:{} result:{}", tblUser.getAppUserId(), JSONUtil
						.toJsonStr(onSaleBenefitPackageRequest), JSONUtil.toJsonStr(packageDto)));
		long packageId = packageDto.getPackageId();
		goodsInfo.setGoodsId(String.valueOf(packageId));
		goodsInfo.setGoodsName(packageDto.getPackageName());
		goodsInfo.setGoodsNums(request.getPurchaseAmount());
		goodsInfo.setGoodsType("0");

		// 分布式锁同步（用户+权益包）
		String lockKey = String.format(RedisLockUtil.LOCK_KEY_USER_BENEFIT_PACKAGE_PURCHASE, tblUser.getAppUserId(), packageId);
		String lockValue = UUID.randomUUID().toString();
		boolean lock = false;
		try {
			lock = redisLockUtil.acquireLock(lockKey, lockValue, 1, TimeUnit.MINUTES);
			if (lock) {
				// 检查当前用户是否存在未支付完成订单，如果存在则返回现有订单
				GetUnPayBenefitPurchaseRecordRequest unPayBenefitPurchaseRecordRequest = new GetUnPayBenefitPurchaseRecordRequest();
				unPayBenefitPurchaseRecordRequest.setPackageId(packageId);
				TblBenefitPurchaseRecordDto queryPurchaseRecordResult = iInnerService
						.getUnPayBenefitPurchaseRecord(tblUser.getAppUserId(), unPayBenefitPurchaseRecordRequest);
				if (Objects.isNull(queryPurchaseRecordResult)) {
					log
							.error("NftVipPayOrderCreator createPayOrder getUnPayBenefitPurchaseRecord failed. uid:{} param:{} result:{}", tblUser
									.getAppUserId(), JSONUtil.toJsonStr(unPayBenefitPurchaseRecordRequest), JSONUtil.toJsonStr(queryPurchaseRecordResult));
					throw new ChainException(ErrorMessage.ORDER_CREATE_FAILED);
				}

				log
						.info("NftVipPayOrderCreator createPayOrder uid:{} queryPurchaseRecordResult:{}", tblUser.getAppUserId(), JSONUtil
								.toJsonStr(queryPurchaseRecordResult));
				goodsInfo.setGoodsPrice(queryPurchaseRecordResult.getUnitPrice());
				TblPayOrder payOrderBusiness = iTblPayOrderService.getByBusiness(orderTypeEnum, String.valueOf(queryPurchaseRecordResult.getRecordId()));
				if (payOrderBusiness != null) {
					log.info("NftVipPayOrderCreator createPayOrder uid:{} current payOrder:{}", tblUser.getAppUserId(), JSONUtil.toJsonStr(payOrderBusiness));
					PayCenterArgsResponse response = payCenterHttpService
							.buildCreateOrderParams(payOrderBusiness, tblUser, request.getBackUrl(), (AssetAllInfoResponse) null);
					response.setCreateNewOrder(false);
					return response;
				} else {
					log.error("NftVipPayOrderCreator find PurchaseRecord without payOrder.purchaseRecordId:{}", queryPurchaseRecordResult.getRecordId());
				}

				long purchaseRecordId = IdUtil.getSnowflakeNextId();
				// 新建支付订单
				log.info("NftVipPayOrderCreator createPayOrder start create pay order uid:{} request:{}", tblUser.getAppUserId(), JSONUtil.toJsonStr(request));
				BigDecimal unitPrice = packageDto.getSalePrice();
				// 测试用户重置单价
				unitPrice = nftOrderService.testUserResetPrice(tblUser.getAppUserId(), unitPrice);
				goodsInfo.setGoodsPrice(unitPrice);
				BigDecimal actualPayMoney = unitPrice.multiply(new BigDecimal(request.getPurchaseAmount()));
				PayAttach payAttach = new PayAttach();
				payAttach.setBenefitPackageId(packageId);
				TblPayOrder payOrder = nftOrderService
						.saveUnPayOrder(orderTypeEnum, String.valueOf(purchaseRecordId), actualPayMoney, tblUser.getAppUserId(), JSONUtil.toJsonStr(payAttach));
				// 创建权益包购买记录
				log
						.info("NftVipPayOrderCreator createPayOrder start create benefit purchase record uid:{} request:{}", tblUser.getAppUserId(), JSONUtil
								.toJsonStr(request));
				SaveBenefitPurchaseUnPayDto saveBenefitPurchaseUnPayDto = new SaveBenefitPurchaseUnPayDto();
				saveBenefitPurchaseUnPayDto.setRecordId(purchaseRecordId);
				saveBenefitPurchaseUnPayDto.setPackageId(packageId);
				saveBenefitPurchaseUnPayDto.setPurchaseNum(request.getPurchaseAmount());
				saveBenefitPurchaseUnPayDto.setUnitPrice(unitPrice);
				Long saveUnPayBenefitPurchaseResult = iInnerService.saveUnPayBenefitPurchase(tblUser.getAppUserId(), saveBenefitPurchaseUnPayDto);
				if (Objects.equals(0L, onSaleBenefitPackageRequest)) {
					log
							.error("NftVipPayOrderCreator createPayOrder saveUnPayBenefitPurchase failed. uid:{} param:{} result:{}", tblUser
									.getAppUserId(), JSONUtil.toJsonStr(saveBenefitPurchaseUnPayDto), JSONUtil.toJsonStr(saveUnPayBenefitPurchaseResult));
					throw new ChainException(ErrorMessage.ORDER_CREATE_FAILED);
				}

				// 插入延时取消任务
				nftOrderService.sendPayDelayMessage(payOrder);
				return payCenterHttpService.buildCreateOrderParams(payOrder, tblUser, request.getBackUrl(), (AssetAllInfoResponse) null);
			} else {
				log.warn("createOrder failed. acquire lock on goods and user failed. uid:{} param:{}", tblUser.getAppUserId(), JSONUtil.toJsonStr(request));
			}
		} finally {
			if (lock) {
				redisLockUtil.releaseLock(lockKey, lockValue);
			}
		}
		throw new ChainException(ErrorMessage.UNKNOWN_ERROR);
	}

	/**
	 * 确认支付订单
	 *
	 * @param payOrder
	 * @param callbackMessage
	 * @param appUserId
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean confirmPayOrder(TblPayOrder payOrder, PayCallbackMessage callbackMessage, String appUserId) {
		boolean needRefund = false;
		// 业务订单确认
		GetBenefitPurchaseRecordRequest benefitPurchaseRecordRequest = new GetBenefitPurchaseRecordRequest();
		benefitPurchaseRecordRequest.setRecordId(Long.valueOf(payOrder.getBusinessRefId()));
		TblBenefitPurchaseRecordDto purchaseRecord = iInnerService.getBenefitPurchaseRecord(Long.valueOf(appUserId), benefitPurchaseRecordRequest);
		if (Objects.isNull(purchaseRecord)) {
			log
					.error("NftVipPayOrderOperator confirmPayOrder getBenefitPurchaseRecord failed. uid:{} param:{}", appUserId, JSONUtil
							.toJsonStr(benefitPurchaseRecordRequest));
			throw new ChainException(ErrorMessage.ORDER_OPERATE_FAILED);
		}

		if (Objects.equals(purchaseRecord.getPayStatus(), BenefitPurchasePayStatusEnum.NOT_PAYED.code())) {
			// 更新支付记录
			nftOrderService.savePaySuccessRecord(payOrder, callbackMessage);

			// 更新权益购买记录
			HandlePaySuccessRequest handlePaySuccessRequest = new HandlePaySuccessRequest();
			handlePaySuccessRequest.setRecordId(purchaseRecord.getRecordId());
			Boolean paySuccessResult = iInnerService.handlePaySuccess(Long.valueOf(appUserId), handlePaySuccessRequest);
			if (Objects.equals(paySuccessResult, Boolean.FALSE)) {
				log
						.error("NftVipPayOrderOperator confirmPayOrder handlePaySuccess failed. uid:{} param:{} result:{}", appUserId, JSONUtil
								.toJsonStr(handlePaySuccessRequest), JSONUtil.toJsonStr(paySuccessResult));
				throw new RuntimeException(ErrorMessage.ORDER_OPERATE_FAILED.getMessage());
			}

			// 发送支付成功消息
			rocketMqService.sendPayOrderStatusChangeMsg(payOrder.getPayOrderId());
			return true;
		} else if (Objects.equals(purchaseRecord.getPayStatus(), BenefitPurchasePayStatusEnum.EXPIRE.code())
				|| Objects.equals(purchaseRecord.getPayStatus(), BenefitPurchasePayStatusEnum.CANCEL.code())) {
			if (nftOrderService.alreadyApplyRefund(payOrder.getPayOrderId())) {
				log.warn("支付中心支付成功，业务订单已取消，已申请退款 {}", JSONUtil.toJsonStr(callbackMessage));
			} else {
				// 业务订单已取消，向支付中心申请退款
				needRefund = true;
			}
		} else {
			log.warn("支付处理失败，目标订单不是待支付状态！ {}", JSONUtil.toJsonStr(callbackMessage));
		}

		if (needRefund) {
			nftOrderService.startRefundOrder(payOrder, callbackMessage);
		}
		return false;
	}

	/**
	 * 取消支付订单
	 *
	 * @param payOrder
	 * @param reason
	 */
	@Override
	public void doPayCancel(TblPayOrder payOrder, String reason) {
		// 取消payOrder
		isFalse(iTblPayOrderService
				.updatePayStatus(payOrder.getPayOrderId(), PayOrderStatusEnum.UN_PAY, PayOrderStatusEnum.PAY_CANCEL), ErrorMessage.ORDER_OPERATE_FAILED);

		TblPayOrderLog payOrderLog = TblPayOrderLog
				.builder()
				.payOrderLogId(IdUtil.getSnowflakeNextId())
				.payStatus(PayOrderStatusEnum.PAY_CANCEL.getCode())
				.payOrderId(payOrder.getPayOrderId())
				.createTime(System.currentTimeMillis())
				.detail(reason)
				.build();
		isFalse(iTblPayOrderLogService.save(payOrderLog), ErrorMessage.ORDER_OPERATE_FAILED);

		// 设置权益包购买记录支付结果
		HandlePayCancelRequest handlePayCancelRequest = new HandlePayCancelRequest();
		handlePayCancelRequest.setRecordId(Long.valueOf(payOrder.getBusinessRefId()));
		Boolean payCancelResult = iInnerService.handlePayCancel(Long.valueOf(payOrder.getAppUserId()), handlePayCancelRequest);
		if (Objects.equals(payCancelResult, Boolean.FALSE)) {
			log
					.error("NftVipPayOrderOperator doPayCancel handlePayCancel failed. uid:{} param:{} result:{}", payOrder.getAppUserId(), JSONUtil
							.toJsonStr(handlePayCancelRequest), JSONUtil.toJsonStr(payCancelResult));
			throw new RuntimeException(ErrorMessage.ORDER_OPERATE_FAILED.getMessage());
		}

		// 发送支付订单状态变更消息
		rocketMqService.sendPayOrderStatusChangeMsg(payOrder.getPayOrderId());
	}

	@Override
	public GoodsInfo getGoodsInfo(TblPayOrder payOrder) {
		GoodsInfo goodsInfo = new GoodsInfo();
		PayAttach payAttach = JSONUtil.toBean(payOrder.getBusinessAttach(), PayAttach.class);
		payAttach.getBenefitPackageId();
		goodsInfo.setGoodsId(String.valueOf(payAttach.getBenefitPackageId()));
		goodsInfo.setGoodsName("数藏vip");
		goodsInfo.setGoodsNums(1);
		goodsInfo.setGoodsType("0");
		goodsInfo.setGoodsPrice(payOrder.getPayMoney().setScale(2, RoundingMode.HALF_UP));
		return goodsInfo;
	}
}
